package com.lagou.edu.factory;

import com.lagou.edu.annotation.*;
import org.reflections.Reflections;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author 应癫
 *
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {


    private static Map<String,Object> map = new HashMap<>();  // 存储对象

    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static  Object getBean(String id) {
        return map.get(id);
    }

    public static <T> T  getBeanByType(Class<T> aTypeForGetBean) {
        for (Map.Entry<String, Object> stringObjectEntry : map.entrySet()) {
            Object value = stringObjectEntry.getValue();
            String key = stringObjectEntry.getKey();



            boolean isFind = false;
            if(aTypeForGetBean.isInterface()){
                Class<?>[] interfaces = value.getClass().getInterfaces();
                for (Class<?> anInterface : interfaces) {
                    if(anInterface == aTypeForGetBean){
                        isFind = true;
                        break;
                    }
                }
            }else{
                if(value.getClass() == aTypeForGetBean){
                    isFind = true;
                }
            }
            if (isFind) {
                return (T)map.get(key);
            }

        }
        return null;
    }

    public static  void addBean(String id,Object o) {
        map.put(id,o);
    }


    public static void init(){
        String basePackage = "com.lagou.edu";
        Reflections reflections = new Reflections(basePackage);


        Set<Class<?>> classes = reflections.getTypesAnnotatedWith(ServiceMy.class);
        classes.addAll(reflections.getTypesAnnotatedWith(RepositoryMy.class));
        classes.addAll(reflections.getTypesAnnotatedWith(ComponentMy.class));
        System.out.println("最开始扫描到的class = " + classes);
        for (Class type : classes) {
            Annotation[] annotationsOnType = type.getAnnotations();
            String beanId = null;
            for (Annotation annotation : annotationsOnType) {

                if(annotation instanceof ServiceMy){
                    beanId = ((ServiceMy) annotation).value();
                    break;
                }
                if(annotation instanceof RepositoryMy){
                    beanId = ((RepositoryMy) annotation).value();
                    break;
                }
                if(annotation instanceof ComponentMy){
                    beanId = ((ComponentMy) annotation).value();
                    break;
                }
            }

            Object bean = null;
            if(!beanId.equals("")){
                bean = getBean(beanId);
            }

            if (bean == null) {
                Class[] interfaces = type.getInterfaces();
                Class aTypeForGetBean = null;
                if(interfaces.length > 0){
                    aTypeForGetBean = interfaces[0];
                }else{
                    aTypeForGetBean = type;
                }
                beanId = getInitialLowercase(aTypeForGetBean.getSimpleName());
                bean = getBeanByType(aTypeForGetBean);
            }


            if (bean != null) {
                continue;
            }else{
                bean  = doCreateBean(type,reflections);

                addBean(beanId,bean);
            }
        }
    }


    public static Object doCreateBean(Class type, Reflections reflections){
        Class instanceType = null;
        if(type.isInterface()){
            Set subTypesOf = reflections.getSubTypesOf(type);
            if(subTypesOf.size() > 0){
                instanceType = (Class) subTypesOf.iterator().next();
            }
        }else{
            instanceType = type;
        }
        try {
            Object o = instanceType.newInstance();
            Field[] declaredFields = instanceType.getDeclaredFields();
            for (Field field : declaredFields) {
                Annotation[] annotations = field.getAnnotations();
                for (Annotation annotation : annotations) {
                    if(annotation instanceof AutowiredMy){
                        String beanId = ((AutowiredMy) annotation).value();
                        Object bean = null;
                        if(!beanId.equals("")){
                            bean = getBean(beanId);
                        }
                        Class<?> fieldType = field.getType();
                        if(bean == null){
                            Class aTypeForGetBean = null;
                            if (fieldType.isInterface() || fieldType.getInterfaces().length == 0) {
                                aTypeForGetBean = fieldType;
                            }else{
                                aTypeForGetBean = fieldType.getInterfaces()[0];

                            }
                            beanId = getInitialLowercase(aTypeForGetBean.getSimpleName());
                            bean = getBeanByType(aTypeForGetBean);
                        }
                        if(bean == null){
                            bean = doCreateBean(fieldType,reflections);
                            addBean(beanId,bean);
                        }
                        field.setAccessible(true);
                        field.set(o,bean);
                    }
                }
            }



            TransactionalMy transactionalMy = o.getClass().getAnnotation(TransactionalMy.class);
            if (transactionalMy != null) {
                ProxyFactory proxyFactory = getBeanByType(ProxyFactory.class);
                if(proxyFactory == null){
                    proxyFactory = (ProxyFactory)doCreateBean(ProxyFactory.class,reflections);
                    addBean(getInitialLowercase(proxyFactory.getClass().getSimpleName()),proxyFactory);
                }

                if(o.getClass().getInterfaces().length > 0){
                    o = proxyFactory.getJdkProxy(o);
                }else{
                    o = proxyFactory.getCglibProxy(o);
                }
            }
            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取首字母小写后的字符串
     * @param str
     * @return
     */
    public static String getInitialLowercase(String str){
       return String.valueOf(str.charAt(0)).toLowerCase() + str.substring(1);
    }


}
